package battle

import (
	"fmt"
	"server/game/define/helper"
	"server/social"
	"server/social/graph"

	"github.com/hwcer/cosgo/random"
	"github.com/hwcer/cosgo/registry"
	"github.com/hwcer/cosgo/utils"
	"github.com/hwcer/cosmo/update"
	"github.com/hwcer/logger"
	"github.com/hwcer/updater"
	"github.com/hwcer/yyds/context"
	"github.com/hwcer/yyds/errors"

	"server/game/cache"
	"server/game/itypes"
	"server/game/model"
	"server/game/pkg/rank"
	"server/share/config"
	"sort"
	"sync"
)

/**
 * @name fWboss
 * 好友boss
 */

func init() {
	Register(&FWBoss{})
}

const (
	graphFWBossShare  = "FWBossShare"
	FWBossBattleTimes = "FWBossBattleTime"
)

var lockerFWBoss sync.Mutex

type FWBossShare struct {
	Id     string `json:"id" `
	HP     int64  `json:"hp"`
	Expire int64  `json:"expire"`
}

type FWBoss struct {
}

// 开战记录

func (this *FWBoss) Caller(node *registry.Node, handle *context.Context) interface{} {
	f := node.Method().(func(*FWBoss, *context.Context) interface{})
	return f(this, handle)
}

/**
 * @name openStar
 * @param int id starID
 * 点击流星召唤好友boss
 */

func (this *FWBoss) OpenStar(c *context.Context) interface{} {
	rh := cache.GetRole(c.Player.Updater)
	rv := rh.All()
	if rv.FWbossID == 0 {
		return errors.Error("流星ID不匹配")
	}
	meteorConfig := config.Data.FriendBossMeteor[rv.FWbossID]
	if meteorConfig == nil {
		return errors.ErrConfigEmpty
	}
	rdi := map[int32]int32{}
	for _, v := range meteorConfig.BossList {
		rdi[v.BossID] = v.Val
	}
	rdm := random.New(rdi)
	i := rdm.Roll()
	if i <= 0 {
		return errors.New("流星配置不存在")
	}
	bossConfig := config.Data.FriendBoss[i]

	//reward := itypes.ItemsGroup.Parse(c.node.Updater, bossConfig.OnhookBoxID, 1)
	now := c.Unix()
	data := model.FWBoss{
		Id:     model.ObjectId.Simple(),
		Iid:    bossConfig.BossID,
		Uid:    c.Uid(),
		Kill:   0,
		HP:     bossConfig.ShareMonsterHP,
		Update: now,
		Status: 0,
		Expire: now + int64(bossConfig.AfkEscapeTime),
		Values: map[string]model.FWBossValues{},
	}
	rh.Set("FWbossID", 0)

	c.Player.Updater.On(updater.EventTypeSuccess, func(u *updater.Updater) (next bool) {
		_ = db.Create(&data)
		return false
	})

	return data
}

func (this *FWBoss) GetBossInfo(c *context.Context, rows []*model.FWBoss) any {
	reply := map[string]interface{}{}
	reply["cd"] = c.Player.Values.GetInt64(FWBossBattleTimes)
	reply["rows"] = rows
	return reply
}

/**
 * @name getBoss
 * 获取好友boss (我的BOSS)
 */

func (this *FWBoss) GetBoss(c *context.Context) interface{} {
	var rows []*model.FWBoss
	tx := db.Where("uid=?", c.Uid()).Limit(10)
	//friendBossRecordingTime := config.Data.Base["friendBossRecordingTime"]
	//var val int64
	//if friendBossRecordingTime != nil {
	//	val = int64(friendBossRecordingTime.Val)
	//}
	tx = tx.Where("expire > ?", c.Unix())
	tx = tx.Order("update", -1)
	tx = tx.Omit("values")
	tx = tx.Limit(10)
	tx = tx.Find(&rows)

	if tx.Error != nil {
		return tx.Error
	}
	return this.GetBossInfo(c, rows)
}

/**
 * @name Search
 * 搜索好友共享的BOSS
 * return  map[好友uid]FWBossShare
 */

func (this *FWBoss) Search(c *context.Context) interface{} {
	//reply := map[string]FWBossShare{}
	now := c.Unix()
	var bossId []string
	social.Graph.Range(c.Uid(), graph.RelationFriend, func(g graph.Getter) bool {
		p := g.Player()
		if i := p.Values.Get(graphFWBossShare); i != nil {
			n := i.(*FWBossShare)
			if n.Expire > now && n.HP > 0 {
				bossId = append(bossId, n.Id)
			}
		}
		return true
	})
	if len(bossId) == 0 {
		return nil
	}
	var rows []*model.FWBoss
	tx := db.Where(bossId)
	tx = tx.Where("expire > ?", c.Unix())
	tx = tx.Omit("values")
	tx = tx.Find(&rows)

	if tx.Error != nil {
		return tx.Error
	}

	return this.GetBossInfo(c, rows)
}

/**
 * @name endFindBattle
 * @param string bossID bossID
 * @param int hp 血量百分比,最大30%
 * 结算第一阶段战斗,发现奖励？
 */
func (this *FWBoss) EndFindBattle(c *context.Context) any {
	id := c.GetString("bossID")
	hp := c.GetInt32("hp")
	if id == "" {
		return errors.ErrArgEmpty
	}
	data := &model.FWBoss{}
	if tx := db.Find(data, id); tx.Error != nil {
		return tx.Error
	} else if tx.RowsAffected == 0 {
		return errors.ErrDataNotExists
	}
	if data.Uid != c.Uid() {
		return nil
	}
	if data.Status != model.FWBossStatusNone {
		return c.Error("不能重复发现")
	}
	bossConfig := config.Data.FriendBoss[data.Iid]
	if bossConfig == nil {
		return errors.ErrConfigEmpty
	}
	//是否过期，设置10秒的容错时间
	if data.Expire < c.Unix() {
		return c.Errorf(helper.ErrorFWBossEscape, "怪物已经逃跑了")
	}
	if data.HP <= 0 {
		return c.Errorf(helper.ErrorFWBossDeath, "已经被击杀")
	}

	if hp >= 30 {
		data.HP = data.HP * 30 / 100
		c.Player.Add(bossConfig.OnhookBoxID, 1)
	} else if hp > 0 {
		data.HP -= data.HP * int64(hp) / 100
	}

	if data.HP < int64(bossConfig.MinHP) {
		data.HP = int64(bossConfig.MinHP)
	}
	now := c.Unix()
	data.Update = now
	data.Expire = now + int64(bossConfig.ChallengeTime)
	data.Status = model.FWBossStatusFind
	//data.FindReward = c.Uid()
	if err := db.Select("hp", "update", "status", "expire").Update(data, data.Id).Error; err != nil {
		return err
	}
	return data
}

/**
 * @name shareFriend
 * @param string bossID bossID
 * 分享好友
 **/

func (this *FWBoss) ShareFriend(c *context.Context) any {
	id := c.GetString("bossID")
	if id == "" {
		return errors.ErrArgEmpty
	}
	data := &model.FWBoss{}
	if tx := db.Find(data, id); tx.Error != nil {
		return tx.Error
	} else if tx.RowsAffected == 0 {
		return errors.ErrDataNotExists
	}
	if data.Uid != c.Uid() {
		return errors.ErrDataNotExists
	}
	if data.HP <= 0 {
		return c.Errorf(helper.ErrorFWBossDeath, "已经被击杀")
	}
	if data.Expire < c.Unix() {
		return c.Errorf(helper.ErrorFWBossEscape, "怪物已经逃跑了")
	}
	if err := db.Model(data).Where(data.Id).Set("status", model.FWBossStatusShare).Error; err != nil {
		return err
	}
	_ = social.Graph.Modify(c.Uid(), func(player *graph.Player) error {
		player.Set(graphFWBossShare, &FWBossShare{
			Id:     id,
			HP:     data.HP,
			Expire: data.Expire,
		})
		return nil
	})

	return nil
}

/**
 * @name startBattle
 * @param string bossID bossID
 * 开始战斗
 **/

func (this *FWBoss) StartBattle(c *context.Context) any {
	if cd := c.Player.Values.GetInt64(FWBossBattleTimes); cd > 0 && cd > c.Unix() {
		return c.Error("战斗CD中")
	}

	id := c.GetString("bossID")
	if id == "" {
		return errors.ErrArgEmpty
	}
	data := &model.FWBoss{}
	if tx := db.Find(data, id); tx.Error != nil {
		return tx.Error
	} else if tx.RowsAffected == 0 {
		return errors.ErrDataNotExists
	}

	bossConfig := config.Data.FriendBoss[data.Iid]
	if bossConfig == nil {
		return errors.ErrConfigEmpty
	}
	//是否过期，设置10秒的容错时间
	if (data.Expire + 10) < c.Unix() {
		return c.Errorf(helper.ErrorFWBossEscape, "怪物已经逃跑了")
	}
	if data.HP <= 0 {
		return c.Errorf(helper.ErrorFWBossDeath, "已经被击杀")
	}

	if data.Uid != c.Uid() {
		if data.Status != model.FWBossStatusShare {
			return c.Error("此boss未分享")
		} else if !social.Graph.Has(data.Uid, c.Uid()) {
			return c.Error("你们还不是好友")
		}
	}

	mw := &Middleware{}
	mw.Create(c.Player, MiddlewareTypeFWBoss, bossConfig.BossID, c.Unix()+int64(bossConfig.SingleBattleTime))

	return true
}

/**
 * @name endBattle
 * @param string bossID bossID
 * @param int hp hp
 * @param array unitList 战斗阵容
 * 结束战斗
 **/

type FWBossEndBattleArgs struct {
	ID       string  `json:"bossID"`
	HP       int64   `json:"hp"`
	UnitList []int32 `json:"unitList"`
}

func (this *FWBoss) EndBattle(c *context.Context) any {
	args := &FWBossEndBattleArgs{}
	if err := c.Bind(args); err != nil {
		return err
	}
	if args.ID == "" || args.HP <= 0 || len(args.UnitList) == 0 {
		return errors.ErrArgEmpty
	}
	hp := args.HP
	uid := c.Uid()
	//id := c.GetString("bossID")
	//hp := c.GetInt64("hp")
	//if id == "" || hp <= 0 {
	//	return errors.ErrArgEmpty
	//}
	//结算时要加锁
	lockerFWBoss.Lock()
	defer lockerFWBoss.Unlock()

	dailyColl := itypes.Daily.Handle(c.Player.Updater)

	data := model.NewFWBoss()
	if tx := db.Find(data, args.ID); tx.Error != nil {
		return tx.Error
	} else if tx.RowsAffected == 0 {
		return errors.ErrDataNotExists
	}

	bossConfig := config.Data.FriendBoss[data.Iid]
	if bossConfig == nil {
		return errors.ErrConfigEmpty
	}
	//是否过期，设置10秒的容错时间
	if (data.Expire + 10) < c.Unix() {
		return c.Errorf(helper.ErrorFWBossEscape, "怪物已经逃跑了")
	}
	if data.HP <= 0 {
		return c.Errorf(helper.ErrorFWBossDeath, "已经被击杀")
	}

	mw := &Middleware{}
	if !mw.Verify(c.Player, MiddlewareTypeFWBoss, bossConfig.BossID) {
		return c.Error("你没有开始战斗")
	}
	v, _ := mw.V.(int64)
	if v < c.Unix() {
		return c.Error("战斗超时")
	}

	up := &update.Update{}
	dailyFWboss := utils.CloneMap(dailyColl.Get("FWboss").(map[int32]int32))
	limitConfig := config.Data.FriendBossLimit[bossConfig.Star]
	if dailyFWboss[bossConfig.Star] < limitConfig.Limit {
		//击杀奖励
		if data.HP <= hp {
			data.Kill = c.Unix()
			//data.KillReward = c.Uid()
			up.Set("kill", data.Kill)
			//up.Set("KillReward", data.Kill)
			c.Player.Add(bossConfig.OnhookBoxID, 1)
		}
		//参与奖励
		dailyFWboss[bossConfig.Star] += 1
		c.Player.Add(bossConfig.ShareBoxID, 1)
		dailyColl.Set("FWboss", dailyFWboss)
	}
	up.Inc("hp", -hp)
	vs := data.Values[uid]
	vs.HP += hp
	vs.UnitList = args.UnitList

	up.Set(fmt.Sprintf("values.%s", uid), vs)

	data.HP -= hp
	data.Values[uid] = vs

	if err := db.Model(data).Update(up, data.Id).Error; err != nil {
		return err
	}

	if data.HP <= 0 {
		//邮件奖励
		c.Player.Updater.On(updater.EventTypeSuccess, func(u *updater.Updater) (next bool) {
			mail := model.NewMail(data.Uid, config.Data.MailKeys.FWBossKilled, map[string]any{"id": bossConfig.BossID})
			is := itypes.ItemsGroup.Parse(u, bossConfig.OwnerBox, 1)
			for k1, v1 := range is {
				mail.AddAttr(k1, v1)
			}
			if err := mail.Save(data.Id); err != nil {
				logger.Alert("FWBOSS击杀邮件发送失败:%v", mail)
			}
			return false
		})

	}
	if data.Status == model.FWBossStatusShare {
		_ = social.Graph.Modify(data.Uid, func(p *graph.Player) error {
			if data.HP <= 0 {
				delete(p.Values, graphFWBossShare)
			} else {
				if i := p.Get(graphFWBossShare); i != nil {
					b := i.(*FWBossShare)
					b.HP = data.HP
				}
			}
			return nil
		})
	}
	cd := c.Unix() + int64(bossConfig.SingleBattleTime)
	//战斗内置cd
	c.Player.Updater.On(updater.EventTypeSuccess, func(u *updater.Updater) (next bool) {
		c.Player.Values.Set(FWBossBattleTimes, cd)
		return false
	})

	return map[string]any{"cd": cd, "data": data}
}

/**
 * @name getReward
 * @param string bossID bossID
 * 获取奖励
 **/

func (this *FWBoss) GetReward(c *context.Context) any {
	return c.Error("这个接口的原始代码和EndBattle一模一样，暂时不清楚什么作用")
}

/**
 * @name getAllReward
 * 一键获取奖励(有风险的功能)
 **/
func (this *FWBoss) GetAllReward(c *context.Context) any {
	return c.Error("所有奖励都在战斗结算时已经发放为什么还需要这种接口》？")
}

/**
 * @name delBoss
 * @param string bossID bossID
 * 删除boss 。。。。实际操作是取消分享
 **/

func (this *FWBoss) DelBoss(c *context.Context) any {
	id := c.GetString("bossID")
	if id == "" {
		return errors.ErrArgEmpty
	}
	data := &model.FWBoss{}
	if tx := db.Select("status").Find(data, id); tx.Error != nil {
		return tx.Error
	} else if tx.RowsAffected == 0 {
		return errors.ErrDataNotExists
	}
	if data.Uid != c.Uid() {
		return errors.ErrDataNotExists
	}
	if data.Status != model.FWBossStatusFind {
		return true
	}
	if err := db.Model(data).Where(data.Id).Set("status", model.FWBossStatusDelete).Error; err != nil {
		return err
	}

	_ = social.Graph.Modify(c.Uid(), func(p *graph.Player) error {
		if i := p.Get(graphFWBossShare); i != nil {
			b := i.(*model.FWBoss)
			if b.Id == data.Id {
				delete(p.Values, graphFWBossShare)
			}
		}
		return nil
	})

	return true
}

/**
 * @name getRank
 * @param string bossID bossID
 * 获取boss击杀排行榜
 **/

type FWBossRank struct {
	rank.Player
	model.FWBossValues
}

func (this *FWBoss) GetRank(c *context.Context) any {
	id := c.GetString("bossID")
	if id == "" {
		return errors.ErrArgEmpty
	}
	data := &model.FWBoss{}
	if tx := db.Find(data, id); tx.Error != nil {
		return tx.Error
	} else if tx.RowsAffected == 0 {
		return errors.ErrDataNotExists
	}
	var rows []*FWBossRank
	for k, v := range data.Values {
		rows = append(rows, &FWBossRank{
			Player:       rank.Player{Uid: k},
			FWBossValues: v,
		})
	}
	sort.Slice(rows, func(i, j int) bool {
		return rows[i].HP > rows[j].HP
	})
	for i, row := range rows {
		row.Rank = int64(i)
	}
	return rows
}

/**
 * @name useItem
 * @param int itemID 道具ID
 * 使用道具发现好友boss
 **/
func (this *FWBoss) UseItem(c *context.Context) any {
	itemID := c.GetInt32("itemID")
	if itemID != 60140001 {
		return errors.ErrArgEmpty
	}
	dailyColl := itypes.Daily.Handle(c.Player.Updater)
	dailyNum := dailyColl.GetInt32("useBossItem")
	if dailyNum >= 5 {
		return c.Error("每天只能使用5次")
	}

	roleDoc := cache.GetRole(c.Player.Updater)
	roleAll := roleDoc.All()
	meteorID := int32(0)
	for _, sc := range config.Data.MeteorGroup {
		if sc.Type == 2 && sc.EffectStage <= roleAll.BattlePoint && sc.MeteorID > meteorID {
			meteorID = sc.MeteorID
		}
	}
	if meteorID == 0 {
		return c.Error("数据错误")
	}
	roleDoc.Set("FWbossID", meteorID)
	c.Player.Sub(itemID, 1)
	dailyColl.Add("useBossItem", 1)
	return true
}
